const db = require('../db')
const moment = require('moment')
const _ = require('lodash')
const bookedDateService = require('services/bookedDate')
const Order = require('models/Order')
const pagify = require('utils/pagify')

const ORDER_STATUS = {
  INVALID: 0,
  WAIT_PAY: 1,
  PAIED: 2,
  CANCELED: 3,
  REFUNED: 4
}

/**
 * 增删改查
 */
const get = async (id) => {
  const order = await Order.where('id', id).fetch({withRelated: ['user', 'house']})
  return order ? order.toJSON() : null
}

const create = async (order, house) => {
  [
    'houseId',
    'unitPrice',
    'count',
    'orderPrice',
    'periodType',
    'startTime',
    'endTime',
    'userId'
  ].forEach((key) => {
    if (!order[key]) {
      throw new Error(`${key} is empty`)
    }
  })
  
  const result = await db('Order')
    .returning('id')
    .insert(order)

  order.id = result[0]
  return order
}

const search = async (query, pageIndex=1, pageSize=10) => {
  const { houseId } = query
  const q = await Order.query((qb) => {
    if (houseId) {
      qb.where('houseId', houseId)  
    }
  })
  .orderBy('-id')

  return pagify.bookshelf(q, pageIndex, pageSize, ['user', 'house'])
}

const cancelExpiredOrders = async () => {
  const orders = await Order.query((qb) => {
    qb.where('status', ORDER_STATUS.WAIT_PAY)
    qb.where('addTime', '<', new Date(new Date() - 15 * 60 * 1000))
  }).fetchAll()

  orders.forEach(async (order, i) => {
    await order.save({
      status: ORDER_STATUS.CANCELED,
      cancelReason: '超时自动关闭'
    })
  })
}

const getOrdersByDate = async (houseId, startTime) => {
  const start = moment(startTime).startOf('day').toDate()
  const end = moment(startTime).endOf('day').toDate()

  const orders = await Order.query((qb) => {
    qb.where('houseId', houseId)
    qb.whereBetween('startTime', [start, end])
  }).fetchAll({ withRelated: 'user' })

  return orders
}

const getOrdersByMonth = async (year, month) => {
  const date = new Date(`${year}/${month}/`)
  const start = moment(date).startOf('month').toDate()
  const end = moment(date).endOf('month').toDate()

  const orders = await Order.query((qb) => {
    qb.where('status', ORDER_STATUS.PAIED)
    qb.whereBetween('addTime', [start, end])
  }).fetchAll({ withRelated: 'house' })
  return orders
}

// TODO: 加cache
const getOrderMonthlyIncome = async (year) => {
  const results = []
  for (let i = 0; i < 12; i++) {
    let orders = await getOrdersByMonth(year, i + 1)
    let total = _.reduce(orders.toJSON().map(order => order.paymentPrice), (a, b) => {
      return a + b
    }, 0)
    results.push(total)
  }
  return results
}

/**
 * 支付相关
 */
const pay = async (orderId, paymentPrice) => {
  const current = new Date()
  const results = await db('Order')
    .where('id', '=', orderId)
    .update({
      status: ORDER_STATUS.PAIED, // 已支付
      paymentPrice: paymentPrice,
      updateTime: current,
      paymentTime: current
    })
  return results[0]    
}

/**
 * 从回调message获取orderId 
 */
const idFromMessage = (message) => {
  const { out_trade_no } = message
  return message.out_trade_no.split('_')[1]
}

/**
 * 处理回调消息
 */
const handleNotify = async (message) => {
  const order_id = idFromMessage(message)
  const { total_fee } = message
  await pay(order_id, total_fee / 100)
}

/**
 * 时段中是否有订单
 */
const checkPeriodHasOrder = async (houseId, startTime, endTime) => {
  const results = await db('Order')
  .where('houseId', '=', houseId)
  .whereBetween('startTime', [startTime, endTime])
  .orWhereBetween('endTime', [startTime, endTime])
  .select()

  return results.length > 0
}

const updateDateStatus = async (houseId, startTime) => {
  await bookedDateService.addBooked(houseId, startTime.getFullYear(), startTime.getMonth() + 1, startTime.getDate())
}

const setPrepayId = async (id, prepayId) => {
  await db('Order')
    .where('id', id)
    .update({
      prepayId
    })
}

module.exports = {
  // JOB
  cancelExpiredOrders,

  // 下单
  checkPeriodHasOrder,
  updateDateStatus,
  
  // CRUD
  create,
  get,
  search,
  getOrdersByDate,
  getOrdersByMonth,
  getOrderMonthlyIncome,

  // 接受回调
  idFromMessage,
  handleNotify,
  setPrepayId
}